package io.chance.receiverlei.channel;

import io.chance.receiverlei.db.RedisHelper;
import io.chance.receiverlei.db.StationStatus;
import io.chance.receiverlei.db.redis.StationService;
import io.chance.receiverlei.main.AppStart;
import lrq.common.MyLog;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.BigEndianHeapChannelBuffer;
import org.jboss.netty.channel.Channel;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

public class ChannelManager {

    static Logger log = MyLog.getLogger(AppStart.connectLogName);

    static HashMap<String, Channel> channelMap = new HashMap<>();

    /**
     * 根据channelid获取设备唯一编码
     *
     * @param channelid
     * @return
     */
    public static String getMN(String channelid) {
        String MN = RedisHelper.getInstance().getMn(channelid);
        return MN;
    }

    /**
     * 根据设备唯一编码获取channelid
     *
     * @param MN
     * @return
     */
    public static String getChannelIdByMN(String MN) {
        String channelId = RedisHelper.getInstance().getChannelId(MN);
        return channelId;
    }

    /**
     * 获取连接信息
     * gislin
     * 2016年4月15日
     *
     * @return
     */
    public static HashMap<String, Channel> getChannelMap() {
        return channelMap;
    }

    /**
     * 移除设备
     * 从redis中移除channelid和设备编号信息，并把对应的数采仪状态改为离线
     * gislin
     * 2015年10月26日
     *
     * @param channelid
     * @throws Exception
     */
    public static void removeChannel(String channelid) {

        if (!channelMap.containsKey(channelid)) {
            return;
        }
        Channel channel = channelMap.get(channelid);
        if (channel != null) {
            channel.close();
        }
        channelMap.remove(channelid);
        RedisHelper.getInstance().deleteChannel(channelid);
    }

    /**
     * 接入设备
     * 把channelid和设备编号存入redis中，并把对应的数采仪状态改为在线
     * gislin
     * 2015年10月26日
     *
     * @param mn
     * @param channel
     * @throws Exception
     */
    public static void addDevice(String mn, Channel channel) {
        try {

            if (mn == null) {
                String channelid = getChannelId(channel);
                if (channelMap.containsKey(channelid)) {
                    // 如果连接已存在，要先关掉，否则会变成僵尸连接
                    Channel oldChannel = channelMap.get(channelid);
                    oldChannel.close();
                }
                channelMap.put(channelid, channel);
                RedisHelper.getInstance().addDevice(mn, channelid);
                return;
            }

            String oldChannelId = RedisHelper.getInstance().getChannelId(mn);
            String incomeChannelId = getChannelId(channel);

            if (oldChannelId == null) {
                RedisHelper.getInstance().addDevice(mn, incomeChannelId);
                updateDeviceActivityTime(mn);
                log.info("设备:" + mn + ",IP:" + channel.getRemoteAddress().toString() + ",ID:" + ChannelManager.getChannelId(channel) + ",已接入");
            }
            else if (oldChannelId != null && !oldChannelId.equals(incomeChannelId)) {
                ChannelManager.removeChannel(oldChannelId);
                RedisHelper.getInstance().addDevice(mn, incomeChannelId);
                updateDeviceActivityTime(mn);
                log.info("设备:" + mn + ",IP:" + channel.getRemoteAddress().toString() + ",以新连接接入。");
                return;
            }
            else if (oldChannelId != null && oldChannelId.equals(incomeChannelId)) {
                ChannelManager.updateDeviceActivityTime(mn);
            }
        }
        catch (Exception e) {
            log.error("注册设备出错", e);
        }
    }

    /**
     * 获取channelid
     * gislin
     * 2016年1月28日
     *
     * @param channel
     * @return
     */
    public static String getChannelId(Channel channel) {
        String id = channel.getId() + "_" + channel.getRemoteAddress().toString().replace(":", "@").replace("/", "");
        return id;
    }

    /**
     * 更新设备最后活动时间
     * gislin
     * 2015年10月26日
     *
     * @param MN
     */
    public static void updateDeviceActivityTime(String MN) {
        RedisHelper.getInstance().setDeviceActivityTime(MN);
    }

    /**
     * 给指定的channel发送消息
     * gislin
     * 2015年10月29日
     *
     * @param channelId
     * @param msg
     */
    public static void sendMsgByChannelId(String channelId, String msg) {

        Channel channel = channelMap.get(channelId);

        if (channel != null) {
            channel.write(msg);
        }
    }

    /**
     * 给指定设备发送消息，心跳和注册信息不能通过此方法发送
     * gislin
     * 2015年10月29日
     *
     * @param mn
     * @param msg
     */
    public static void sendMsgByMN(String mn, String msg) {
        String channelId = RedisHelper.getInstance().getChannelId(mn);

        Channel channel = channelMap.get(channelId);
        if (channel != null && channel.isConnected()) {
            channel.write(msg);
        }
    }

    /**
     * 给指定设备发送消息，心跳和注册信息不能通过此方法发送
     * gislin
     * 2015年10月29日
     *
     * @param mn
     */
    public static void sendByteMsgByMN(String mn, byte[] data) {
        String channelId = RedisHelper.getInstance().getChannelId(mn);

        Channel channel = channelMap.get(channelId);
        BigEndianHeapChannelBuffer wb = new BigEndianHeapChannelBuffer(data);
        if (channel != null && channel.isConnected()) {
            channel.write(wb);
        }
    }

    public static void ChannelOnLineChecker() {
        deviceOnLineCheck();

//        checkChannelEnable(); //不立即发送心跳
    }

    /**
     * 检查所有channel的连接情况，把长时间无响应的channel踢掉
     * gislin
     * 2015年10月29日
     *
     * @throws InvocationTargetException
     * @throws IllegalArgumentException
     * @throws SecurityException
     * @throws NoSuchMethodException
     */
    public static void deviceOnLineCheck() {
        int timeOut = 60 * 1000 * 5; //
        Date currentTime = new Date();
        List<String> devices = RedisHelper.getInstance().getDevices();
        for (String mn : devices) {
            Date lastActivityTime = RedisHelper.getInstance().getDeviceActivityTime(mn);
            boolean needRemove = false;
            if (lastActivityTime == null) {
                needRemove = true;
            }
            else {
                double timeDiff = currentTime.getTime() - lastActivityTime.getTime();
                if (timeDiff >= timeOut) {
                    needRemove = true;
                }
            }
            if (needRemove) {
                String channelid = RedisHelper.getInstance().getChannelId(mn);
                Channel channel = channelMap.get(channelid);

                log.info("设备离线移除:" + mn);

                if (channel != null) {
                    channel.close();
                }
                RedisHelper.getInstance().deleteChannel(channelid);
                RedisHelper.getInstance().deleteDevice(mn);
                StationService.getInstance().setStationStatus(StationStatus.fault, mn);
            }
        }
    }

    /**
     * 检查所有的连接id，如果有没有与之关联的设备，则关闭，并删除
     * gislin
     * 2016年3月25日
     */
    public static void checkChannelEnable() {
        List<String> channelIds = RedisHelper.getInstance().getChannelid();
        for (String channelId : channelIds) {
            String mn = RedisHelper.getInstance().getMn(channelId);
            if (mn == null || mn.trim().equals("") || mn.equals("null")) {
                Channel channel = channelMap.get(channelId);
                if (channel != null) {
                    log.info("该通道没有设备信息绑定，移除：" + channelId);
                    channel.close();
                }
                RedisHelper.getInstance().deleteChannel(channelId);
            }
        }
    }

}
